#ifndef MATRICE3X3_HPP
#define MATRICE3X3_HPP

/** Fichier qui implémente une classe représentant les matrices réelles 3x3.
 *
 * Ce fichier contient la définition de la classe Matrice3x3.
 *
 * @file Matrice3x3.hpp
 * @author Johan "Solo" Luisier
 *
 * @date 24 Juin 2011
 */

#include "Vecteur.hpp"

/*
 * La classe de mère est définie ici.
 */
#include <boost/numeric/ublas/matrix.hpp>

/** Alias pour raccourcir les déclarations.
 *
 * Le type utilisé comme base implémente déjà la plupart des méthodes d'algèbre
 * linéaire, ce qui le rend très intéressant, mais le tapper est fastidieux.
 */
typedef boost::numeric::ublas::matrix< double > MatriceBase;

double LeviCivita( const unsigned& i, const unsigned& j, const unsigned k );

/** Classe qui implémente les matrices réelles @f$ 3 \times 3 @f$.
 *
 * Les matrices 3x3 à coefficients réels forment le groupe \f$M \left(
 * 3, \mathbb{R} \right)\f$. Cette classe implémente de telles matrices
 * 3x3. Hérite de matrix< double > (de la librairie boost) afin de
 * simplifier la gestion des indices et de la mémoire.
 *
 * Toutes les opérations de base sont implémentées ici : addition,
 * soustraction, affectation, transposition, calcul de déterminant,
 * multiplication à gauche / à droite par un vecteur.
 *
 * Matrice3x3 n'est pas autorisé à faire une opération impliquant
 * explicitement un VecteurU (vecteur unitaire), car la norme n'est
 * pas conservée.
 *
 * @author Johan "Solo" Luisier
 *
 * @date 24 Juin 2011
 *
 * @class Matrice3x3 "Matrice3x3.hpp" "Algebre/Matrice3x3.hpp"
 */
class Matrice3x3 : public MatriceBase
{
public:
    Matrice3x3( const double& scalaire = 1. );
    Matrice3x3( const VecteurBase& xPrime,
                const VecteurBase& yPrime,
                const VecteurBase& zPrime );
    Matrice3x3( const double& m11, const double& m22, const double& m33 );
    Matrice3x3( const Matrice3x3& mat );
    virtual ~Matrice3x3();

    Matrice3x3& operator=( const Matrice3x3& mat );

    const double& at( const unsigned& i, const unsigned& j ) const;

    double determinant() const;
    double trace() const;
    double cofacteur( const unsigned& i, const unsigned& j ) const;
    Matrice3x3 comatrice() const;

    Matrice3x3 operator-() const;

    Matrice3x3 transpose() const;

    /*****************************************************************************
   *
   * Opérateurs matriciels
   *
   ****************************************************************************/
    Matrice3x3& operator+=(const Matrice3x3& mat);
    Matrice3x3& operator-=(const Matrice3x3& mat);
    Matrice3x3& operator*=(const Matrice3x3& mat);
    Matrice3x3 operator+(const Matrice3x3& mat) const;
    Matrice3x3 operator-(const Matrice3x3& mat) const;
    Matrice3x3 operator*(const Matrice3x3& mat) const;

    /*****************************************************************************
   *
   * Opérateurs sur les scalaires
   *
   ****************************************************************************/
    Matrice3x3& operator*=( const double& scalaire );
    Matrice3x3 operator*( const double& scalaire ) const;
    /**
   * Permet d'écrire indifféremment \f$ M \cdot a \f$ ou \f$ a \cdot M
   * \f$. Utilise en interne la méthode Matrice3x3::operator*(const
   * double& a).
   *
   * @param[in] a scalaire multipliant l'instance de matrice.
   * @param[in] mat matrice à multiplier.
   *
   * @return le résultat de la multiplication \f$ a \cdot \text{mat}
   * \f$.
   */
    friend Matrice3x3 operator*(const double& a, const Matrice3x3& mat);

    /*****************************************************************************
   *
   * Opérateurs sur les points de l'espace
   *
   ****************************************************************************/
    Coord operator*(const Coord& pt) const;
    /**
   * Permet d'obtenir la multipication \e à \e droite d'un point de
   * l'espace par une matrice.
   *
   * Fait appel à la fonction prod.
   *
   * @param[in] pt point de l'espace, qui sera modifié.
   * @param[in] mat matrice par laquelle on veut multiplier \e à \e
   * gauche le point \c pt.
   *
   * @return une référence sur \c pt.
   */
    friend Coord& operator*=(Coord& pt, const Matrice3x3& mat);
    /**
   * Permet d'obtenir le résultat de la multipication \e à \e droite
   * d'un point de l'espace par une matrice.
   *
   * Fait appel à la fonction operator*= pour une instance temporaire.
   *
   * @param[in] pt point de l'espace.
   * @param[in] mat matrice par laquelle on veut multiplier \e à \e
   * gauche le point \c pt.
   *
   * @return un nouveau point de l'espace, égal à la multiplication
   * \f$ \vec{OP} \, M \f$.
   */
    friend Coord operator*(const Coord& pt, const Matrice3x3& mat);

    /*****************************************************************************
   *
   * Opérateurs sur les vecteurs de l'espace
   *
   ****************************************************************************/
    Vecteur operator*(const Vecteur& vec) const;
    /**
   * Permet d'obtenir la multipication \e à \e droite d'un vecteur de
   * l'espace par une matrice.
   *
   * Fait appel à la fonction prod.
   *
   * @param[in] vec vecteur de l'espace, qui sera modifié.
   * @param[in] mat matrice par laquelle on veut multiplier \e à \e
   * gauche le vecteur \c vec.
   *
   * @return une référence sur \c vec.
   */
    friend Vecteur& operator*=(Vecteur& vec, const Matrice3x3& mat);
    /**
   * Permet d'obtenir le résultat de la multipication \e à \e droite
   * d'un vecteur de l'espace par une matrice.
   *
   * Fait appel à la fonction operator*= pour une instance temporaire.
   *
   * @param[in] vec vecteur de l'espace.
   * @param[in] mat matrice par laquelle on veut multiplier \e à \e
   * gauche le vecteur \c vec.
   *
   * @return un nouveau vecteur de l'espace, égal à la multiplication
   * \f$ \vec{v} \, M \f$.
   */
    friend Vecteur operator*(const Vecteur& vec, const Matrice3x3& mat);

    //VecteurU operator*(const VecteurU& vec) const;
    //friend VecteurU& operator*=(VecteurU& vec, const Matrice3x3& mat);
    //friend VecteurU operator*(const VecteurU& vec, const Matrice3x3& mat);

    /**
   * Permet d'afficher simplement les neuf composantes de la matrice
   * (sous une forme de matrice) dans un flot quelconque.
   *
   * @param[in] os flot de sortie.
   * @param[in] mat matrice à afficher.
   *
   * @return un référence sur le flot utilisé pour l'écriture.
   */
    friend std::ostream& operator<<(std::ostream& os, const Matrice3x3& mat);
private:
    Matrice3x3& operator=( const MatriceBase& mat );
};

#endif // MATRICE3X3_HPP
